package com.oig.sys.base.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysPageInfo;
import com.oig.common.module.SysRequest;
import com.oig.sys.base.constants.SysBaseConstants;
import com.oig.sys.base.constants.SysBaseEnum;
import com.oig.sys.base.dao.ITbCountryDao;
import com.oig.sys.base.dto.CountryDto;
import com.oig.sys.base.entity.TbCountry;
import com.oig.sys.base.service.ITbCountryService;
import com.oig.sys.base.vo.CountryVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wuxl
 */
@Slf4j
@Service
public class TbCountryServiceImpl implements ITbCountryService {

	private final ITbCountryDao tbCountryDao;

	@Autowired
	public TbCountryServiceImpl(ITbCountryDao tbCountryDao){
		 this.tbCountryDao = tbCountryDao;
	}

	@Transactional
	@Override
	public CountryVo addTbCountry(CountryDto countryDto) {
		TbCountry country = this.tbCountryDao.getByCode(countryDto.getCode()) ;
		if (country!=null){
			throw new BadRequestException("编码重复") ;
		}
		country = new TbCountry();
		BeanUtils.copyProperties(countryDto, country);
		if (StringUtils.isNotBlank(countryDto.getParentCode()) && !SysBaseConstants.DEF_PARENT_CODE.equals(countryDto.getParentCode())){
            TbCountry parent = this.tbCountryDao.getByCode(countryDto.getParentCode());
            if (parent==null){
                throw new BadRequestException("上级id不存在");
            }
            if (SysBaseEnum.AreaType.LAND.getCode().equals(parent.getAreaType())){
            	country.setAreaType(SysBaseEnum.AreaType.COUNTRY.getCode());
			} else if (SysBaseEnum.AreaType.COUNTRY.getCode().equals(parent.getAreaType())){
				country.setAreaType(SysBaseEnum.AreaType.CITY.getCode());
			} else {
            	throw new BadRequestException("不能再加下级");
			}
            parent.setSubIndex(parent.getSubIndex()+1);
            this.tbCountryDao.save(parent);
        } else {
            country.setParentCode("0");
            country.setAreaType(SysBaseEnum.AreaType.LAND.getCode());
        }
		country.setSubIndex(0);
		country.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		this.tbCountryDao.save(country) ;

		return buildVo(country);
	}

	private CountryVo buildVo(TbCountry country){
		CountryVo countryVo = new CountryVo();
		BeanUtils.copyProperties(country, countryVo);
		return countryVo;
	}


	@Transactional
	@Override
	public CountryVo updateTbCountry(CountryDto countryDto) {
		TbCountry country = this.tbCountryDao.getByCode(countryDto.getCode()) ;
		if (country==null){
			throw new BadRequestException("编码不存在") ;
		}
		BeanUtils.copyProperties(countryDto, country,"code", "status", "parentCode","areaType");
		this.tbCountryDao.save(country) ;
		return buildVo(country);
	}

	@Transactional
	@Override
	public void cancelTbCountry(String code) {
		TbCountry country = this.tbCountryDao.getByCode(code) ;
		if (country==null){
			throw new BadRequestException("国别地区不存在") ;
		}
		if (CommonEnum.CommonStatusEnum.DEL.getCode().equals(country.getStatus())){
			throw new BadRequestException("国别地区已是删除状态") ;
		}
		country.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
		this.tbCountryDao.save(country);
	}

	@Override
	public CountryVo getCountryByCode(String code) {
		TbCountry country = this.tbCountryDao.getByCode(code) ;
		if (country==null) return null;
		return buildVo(country);
	}

	@Override
	public CountryVo getCountryByApplyCode(String applyCode) {
		TbCountry country = this.tbCountryDao.getByApplyCode(applyCode) ;
		if (country==null) return null;
		return buildVo(country);
	}


	@Override
	public List<CountryVo> findCountriesByNames(List<String> nameList) {
		List<CountryVo> voList = new ArrayList<>();
		List<TbCountry> list = this.tbCountryDao.findCountriesByNames(nameList);
		if (list!=null){
			list.forEach(tbCountry -> voList.add(this.buildVo(tbCountry)));
		}
		return voList;
	}

	@Override
	public List<CountryVo> findCountriesByApplyCodes(List<String> applyCodeList) {
		List<CountryVo> voList = new ArrayList<>();
		List<TbCountry> list = this.tbCountryDao.findCountriesByApplyCodes(applyCodeList);
		if (list!=null){
			list.forEach(tbCountry -> voList.add(this.buildVo(tbCountry)));
		}
		return voList;
	}

	@Override
	public CountryVo getCountryByName(String name) {
		TbCountry country = this.tbCountryDao.getByName(name) ;
		if (country==null) return null;
		return buildVo(country);
	}

	@Override
	public CountryVo getCountryByIsoCode(String isoCode) {
		TbCountry country = this.tbCountryDao.getByIsoCode(isoCode) ;
		if (country==null) return null;
		return buildVo(country);
	}

	@Override
	public List<CountryVo> listCountry(String parentCode) {
		return this.tbCountryDao.findByParentCode(parentCode);
	}


	@Override
	public SysPageInfo<CountryVo> findCountryPage(SysRequest<CountryDto> countryRequest) {
		Pageable pageable = countryRequest.pageable();
		Page<TbCountry> countryPage ;
		if (countryRequest.getParamDto()!=null){
			Specification<TbCountry> specification = new Specification<TbCountry>() {
				@Override
				@Nullable
				public Predicate toPredicate(@NonNull Root<TbCountry> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
					List<Predicate> predicateList = new ArrayList<>();
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getParentCode())) {
						Path<String> parentCodePath = root.get("parentCode");
						predicateList.add(criteriaBuilder.equal(parentCodePath, countryRequest.getParamDto().getParentCode()));
					}
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getName())) {
						Path<String> namePath = root.get("name");
						predicateList.add(criteriaBuilder.like(namePath, "%" + countryRequest.getParamDto().getName() + "%"));
					}

					if (CollUtil.isNotEmpty(countryRequest.getParamDto().getCodeList())) {
						Path<String> codePath = root.get("code");
						CriteriaBuilder.In<String> cbIn = criteriaBuilder.in(codePath);
						countryRequest.getParamDto().getCodeList().forEach(cbIn::value);
						predicateList.add(cbIn);
					}

					if (StrUtil.isNotBlank(countryRequest.getParamDto().getCode())) {
						Path<String> codePath = root.get("code");
						predicateList.add(criteriaBuilder.equal(codePath, countryRequest.getParamDto().getCode()));
					}
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getApplyCode())) {
						Path<String> applyCodePath = root.get("applyCode");
						predicateList.add(criteriaBuilder.equal(applyCodePath, countryRequest.getParamDto().getApplyCode()));
					}
					Path<String> statusPath = root.get("status");
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getStatus())) {
						log.debug("status cond:{}", countryRequest.getParamDto().getStatus());
						predicateList.add(criteriaBuilder.equal(statusPath, countryRequest.getParamDto().getStatus()));
					} else {
						predicateList.add(criteriaBuilder.equal(statusPath, CommonEnum.CommonStatusEnum.OK.getCode()));
					}
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getAreaType())) {
						log.debug("areaType cond:{}", countryRequest.getParamDto().getAreaType());
						Path<String> areaTypePath = root.get("areaType");
						predicateList.add(criteriaBuilder.equal(areaTypePath, countryRequest.getParamDto().getAreaType()));
					}
					if (StrUtil.isNotBlank(countryRequest.getParamDto().getGroupCode())) {
						Path<String> groupCodePath = root.get("groupCode");
						predicateList.add(criteriaBuilder.equal(groupCodePath, countryRequest.getParamDto().getGroupCode()));
					}
					criteriaQuery.where(predicateList.toArray(new Predicate[0]));
					return criteriaQuery.getRestriction();
				}
			} ;
			countryPage = this.tbCountryDao.findAll(specification,pageable);
		} else {
			countryPage = this.tbCountryDao.findAll(pageable);
		}

		List<CountryVo> countryVos = BeanUtil.copyToList(countryPage.getContent(), CountryVo.class);
		SysPageInfo<CountryVo> sysPageInfo = new SysPageInfo<>(countryRequest.getPageNo(),
				countryRequest.getPageSize(), countryPage.getTotalElements());
		sysPageInfo.setList(countryVos);
		return sysPageInfo;
	}

	@Override
	public List<CountryVo> findCountry(SysRequest<CountryDto> countryRequest) {
		SysPageInfo<CountryVo> countryPage = this.findCountryPage(countryRequest);
		return countryPage.getList();
	}
}
